package io.kiki.stack.http.feign.jackson.jr;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.jr.ob.JacksonJrExtension;
import com.fasterxml.jackson.jr.ob.api.ExtensionContext;
import com.fasterxml.jackson.jr.ob.api.ReaderWriterProvider;
import com.fasterxml.jackson.jr.ob.api.ValueReader;
import com.fasterxml.jackson.jr.ob.api.ValueWriter;
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * Adapted from https://www.andersaaberg.dk/2020/enable-support-for-java-time-with-jackson-jr/ This
 * adds Java Time support to Jackson JR
 */
public class JavaLocalDateExtension extends JacksonJrExtension {
    @Override
    protected void register(ExtensionContext ctxt) {
        ctxt.insertProvider(new LocalDateReaderWriterProvider());
    }

    private static class LocalDateReaderWriterProvider extends ReaderWriterProvider {
        @Override
        public ValueReader findValueReader(JSONReader readContext, Class<?> type) {
            return type == LocalDate.class ? new LocalDateValueReader() : null;
        }

        @Override
        public ValueWriter findValueWriter(JSONWriter writeContext, Class<?> type) {
            return type == LocalDate.class ? new LocalDateValueWriter() : null;
        }
    }

    private static class LocalDateValueReader extends ValueReader {
        protected LocalDateValueReader() {
            super(LocalDate.class);
        }

        @Override
        public Object read(JSONReader reader, JsonParser p) throws IOException {
            return LocalDate.parse(p.getText(), DateTimeFormatter.ISO_LOCAL_DATE);
        }
    }

    private static class LocalDateValueWriter implements ValueWriter {
        @Override
        public void writeValue(JSONWriter context, JsonGenerator g, Object value) throws IOException {
            context.writeValue(((LocalDate) value).format(DateTimeFormatter.ISO_LOCAL_DATE));
        }

        @Override
        public Class<?> valueType() {
            return LocalDate.class;
        }
    }
}
